<?php
/**
 * Description of basic_model_eav
 *
 * @author:	qfox
 * @package:	core
 * @copyright:	(c) 2010, alex <qfox@ya.ru> yaroshevich aka alex_ez. All rights reserved
 * @see:		core/classes/basic_model.php
 * @link:		http://docs.qfox.ru/model_eav
 * @created:	19.04.2010, 15:14:48
 */
abstract class basic_model_eav extends model {
	
	/**
	 * service variables
	 */
	protected $itemid = null;
	protected $values = null;
	protected $fields = null;
	private $__last_get_by = null;

	protected $default_value = null;

	/**
	 * Constructor
	 *
	 * Creates new object and set default values for empty service variables
	 */
	public function __construct( $value = null )
	{
		parent::__construct( );
		
		$this ->default_value = $value;
		
		// fill values table, fields table and itemid field name
		$this ->values = $this ->values ? $this ->values : ($this ->table.'_values');
		$this ->fields = $this ->fields ? $this ->fields : ($this ->table.'_fields');
		$this ->itemid = $this ->itemid ? $this ->itemid : 'item_id';
	}


	/**
	 * Getters
	 */
	
	public function get( $id, $cacheTime = -1 )
	{
		$r = parent::get( $id, $cacheTime )	;

		$this ->__last_get_by = array( array( 'id' => $id ), null, null );
		return $r;
	}

	public function by( $p = null, $o = null, $l = null, $cacheTime = -1 )
	{
		$this ->checkup_sql( $p, $o, $l );

		$sql = $this ->make_sql( $this ->table, '*', $p, $o, $l );
		
		$r = $this ->db ->_list( $sql, 'id' );
		$r = array_filter( $r );
		$this ->__found_rows = $this->db ->_value( "SELECT FOUND_ROWS();" );
		
		$this ->__last_get_by = array( $p, $o, $l );
		return $r;
	}

	public function fields( $k = null, $v = null )
	{
		$cache = cache::instance()->enable(__METHOD__,'6H');
		$cache_key = 'persons_fields'; //$cache->key(get_class($this),__METHOD__,'');
		
		$r = $cache->$cache_key;
		if ($r===false)
		{
			$sql = $this->make_sql( $this ->fields, '*', array( 'state' => 'on' ) );
			$r = $this->db->_list( $sql, 'id' );
			$r = array_filter( $r );
			$cache->$cache_key = $r;
		}
		
		$cache->disable(__METHOD__);
		
		// prepare
		if( !is_null( $k ) || !is_null( $v ) )
			$r = _a::flat( $r, $k, $v );
		
		return $r;
	}
	
	public function values( $id = null )
	{
		if( is_null( $id ) )
		{
			$a =& $this ->__last_get_by;
			if( is_null( $a ) )
				return null;
			
			return $this ->values_by( $a[0], $a[1], $a[2] );
		}
		
		$r = $this ->values_by( array( 'id' => $id ) );
		return reset( $r );
	}

	public function form_values( $id = null ) {
		$ret = array(
			'fields' => array(),
			'values' => array()
			);

		$r = $this->values($id);

		if(is_array($r)) {

			foreach($r as $field => $values) {
				foreach($values as $v) {
					$ret['fields'][] = $field;
					$ret['values'][] = $v;
				}
			}
		}

		return $ret;
	}

	public function plain_values( )
	{
		$a =& $this ->__last_get_by;
		if( is_null( $a ) )
			return null;
		
		return $this ->plain_values_by( $a[0], $a[1], $a[2] );
	}
	
	public function values_by( $p = null, $o = null, $l = null )
	{
		$r = array( );
		$_r = $this ->plain_values_by( $p, $o, $l );
		
		$fields = $this ->fields( );
		foreach( $_r as $k => $v )
		{
			if( !isset( $fields[$v['field_id']] ) ) continue;
			$tag =& $fields[$v['field_id']]['tag'];
			if( !isset( $r[$v[$this->itemid]][$tag] ) )
				$r[$v[$this->itemid]][$tag] = array( );
			$r[$v[$this->itemid]][$tag][] = $v['value'];
		}
		
		return $r;
	}
	
	public function plain_values_by( $p = null, $o = null, $l = null )
	{
		$this ->checkup_sql( $p, $o, $l );
		
		// mysql5.0 does not support subqueries with limits
		if( !is_null( $l ) )
		{
			$itemids = $this->db ->_list( $this ->make_sql( $this ->table, 'id', $p, $o, $l ), null, 'id' );
			$p = array( $this ->itemid => $itemids );
		}
		else
		{
			$innersql = trim( $this ->make_sql( $this ->table, 'id', $p, $o ), ';' );
			$p = array( $this ->itemid.'@' => $innersql );
		}
		
		$o = 'field_id+,'.$this->itemid.'+,tm+';
		
		$sql = $this ->make_sql( $this ->values, '*', $p, $o );
		$r = $this->db ->_list( $sql );
		$r = array_filter( $r );
		
		return $r;
	}


	/**
	 * add/remove
	 */
	public function add_values( $itemid, $field, $values )
	{
		// check up values existance
		if( !is_array( $values ) )
			$values = array_slice( func_get_args( ), 2 );
		
		$values = array_filter( array_map( 'strval', $values ) );
		if( empty( $values ) )
			return;
		
		$fields = $this ->fields( 'tag', 'id' );
		
		// check up field existance
		if( !isset( $fields[$field] ) )
			throw new model_exception( 'Unknown field tag "'.$field.'"' );
		
		// create value row
		$value_row = $this ->default_value;
		$value_row[ $this ->itemid ] = $itemid;
		$value_row['field_id'] = $fields[$field];
		$value_row['value'] = null;
		
		// make sql values list
		$data = array( );
		foreach( $values as $value )
			if( !empty( $value ) )
			$data[] = array( 'value' => $value );

		if( empty( $data ) )
			return 0;

		$sql = $this->db ->make_insert_values_query( $this ->values, $data, $value_row, 2 );
		$this->db ->exec( $sql );

		$r = ( $this->db ->affected_rows != 0 ? (
			count( $values ) == 1 ? $this->db ->insert_id : true
			) : false );
		
		return $r;
	}

	public function add_form_values( $itemid, $rawdata ) {

		if( !$itemid || !isset($rawdata['values']) )
			return 0;
		
		$data = array( );
		$rawkeys = array_keys($rawdata['values']);
		$fields = $this ->fields( 'tag', 'id' );
		
		foreach($rawkeys as $k) {
			if( !isset($rawdata['fields'][$k]) || !isset($rawdata['values'][$k]) )
				continue;
			
			$field = $rawdata['fields'][$k];
			$value = $rawdata['values'][$k];
			if( empty($field) || empty($value) || !isset($fields[$field]) )
				continue;
			
			$data[$field][] = $value;
		}

		$r = 0;
		foreach( $data as $field => $values )
		{
			$r += $this ->add_values( $itemid, $field, $values );
		}
		
		return $r;

	}

	public function add_plain_value( $data, $id = null )
	{
		if( !is_null( $id ) ) // add id to data
			$data[ $this ->itemid ] = $id;

		$data = $this->db ->make_set_list( $data );

		$sql = "REPLACE INTO %s SET %s;";
		$sql = sprintf( $sql, $this ->values, $data );
		return $sql;
		$this->db ->exec( $sql );

		return $this->db ->affected_rows != 0 ? $this->db ->insert_id : false;
	}

	// todo: rewrite
	public function add_plain_values( $datas, $id = null )
	{
		$r = array( );
		
		foreach( $datas as $k => $data )
		{
			if( !is_null( $id ) ) // add item_id to each data
				$data[ $this ->itemid ] = $id;
			$r[$k] = $this ->add_plain_value( $data );
		}
		
		return $r;
	}
	
	public function remove_value( $id )
	{
		return $this ->remove_values_by( array( 'id' => $id ) );
	}
	
	public function remove_values( $itemid, $field = null, $values = null )
	{
		if( !$itemid ) return false;
		$p = array( );
		$p[$this->itemid] = $itemid;
		
		if( !is_null( $field ) )
		{
			$fields = $this ->fields( 'tag', 'id' );
			if( !isset( $fields[$field] ) )
				throw new model_exception( 'Unknown field tag "'.$field.'"' );
			$p['field_id'] = $fields[$field];
			
			if( !is_null( $values ) )
			{
				if( !is_array( $values ) )
					$values = array_slice( func_get_args( ), 2 );
			}
			$p['value'] = $values;
		}
		
		return $this ->remove_values_by( $p );
	}
	
	public function remove_values_by_id( $ids )
	{
		if( !is_array( $ids ) )
			$ids = array_filter( func_get_args( ) );
		
		if( empty( $ids ) )
			return false;
		
		$ids = $this->db ->make_set_list( array( 'id' => $ids ), 'AND' );
		
		$sql = "DELETE FROM `%s` WHERE %s;";
		$sql = sprintf( $sql, $this ->values, $ids );
		
		$this->db ->exec( $sql );
		
		return $this->db ->affected_rows;
	}

	public function remove_values_by_item_id( $itemid )
	{
		return $this ->remove_values_by( array( $this ->itemid => $itemid ) );
	}
	
	public function remove_values_by( $p )
	{
		$p = $this->db ->make_set_list( $p, 'AND' );
		
		$sql = "DELETE FROM `%s` WHERE %s;";
		$sql = sprintf( $sql, $this ->values, $p );
		
		$this->db ->exec( $sql );
		
		return $this->db ->affected_rows;
	}





}


